From 2d837769a0c0f11b15c45ff4ff18b820299320d8 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 13 Jan 2012 16:43:50 +0000 Subject: [PATCH] wayland: Add basic implementation of GtkClipboard for Wayland This follows the approach used by the Quartz port - that of a separate implementation matching GtkClipboard. The simple clipboard tests in gtk3-demo function correctly but there are almost certainly leaks and bugs. --- gtk/Makefile.am | 7 + gtk/gtkclipboard-wayland.c | 877 +++++++++++++++++++++++++++++++++++++ 2 files changed, 884 insertions(+) create mode 100644 gtk/gtkclipboard-wayland.c diff --git a/gtk/Makefile.am b/gtk/Makefile.am index b8007d857d..ceda3f2953 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -875,8 +875,15 @@ gtk_clipboard_dnd_c_sources = \ gtkclipboard-quartz.c \ gtkdnd-quartz.c else +if USE_WAYLAND +# No wayland gtkdnd-wayland.c yet +gtk_clipboard_dnd_c_sources = \ + gtkclipboard-wayland.c \ + gtkdnd.c +else gtk_clipboard_dnd_c_sources = gtkclipboard.c gtkdnd.c endif +endif # we use our own built_sources variable rules to avoid automake's # BUILT_SOURCES oddities diff --git a/gtk/gtkclipboard-wayland.c b/gtk/gtkclipboard-wayland.c new file mode 100644 index 0000000000..649acbd202 --- /dev/null +++ b/gtk/gtkclipboard-wayland.c @@ -0,0 +1,877 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2000 Red Hat, Inc. + * Copyright (C) 2004 Nokia Corporation + * Copyright (C) 2006-2008 Imendio AB + * Copyright (C) 2011-2012 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include + +#include "gtkclipboard.h" +#include "gtkinvisible.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkintl.h" +#include "gtktextbuffer.h" +#include "gtkselectionprivate.h" + +#include "../gdk/gdk.h" +#include "../gdk/wayland/gdkwayland.h" + +enum { + OWNER_CHANGE, + LAST_SIGNAL +}; + +typedef struct _GtkClipboardClass GtkClipboardClass; + +typedef struct _SetContentClosure SetContentClosure; +struct _GtkClipboard +{ + GObject parent_instance; + + GObject *owner; + GdkDisplay *display; + + SetContentClosure *last_closure; +}; + +struct _GtkClipboardClass +{ + GObjectClass parent_class; + + void (*owner_change) (GtkClipboard *clipboard, + GdkEventOwnerChange *event); +}; + +static void gtk_clipboard_class_init (GtkClipboardClass *class); +static void gtk_clipboard_finalize (GObject *object); +static void gtk_clipboard_owner_change (GtkClipboard *clipboard, + GdkEventOwnerChange *event); + +static GObjectClass *parent_class; +static guint clipboard_signals[LAST_SIGNAL] = { 0 }; + +GType +gtk_clipboard_get_type (void) +{ + static GType clipboard_type = 0; + + if (!clipboard_type) + { + const GTypeInfo clipboard_info = + { + sizeof (GtkClipboardClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_clipboard_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkClipboard), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + clipboard_type = g_type_register_static (G_TYPE_OBJECT, I_("GtkClipboard"), + &clipboard_info, 0); + } + + return clipboard_type; +} + +static void +gtk_clipboard_class_init (GtkClipboardClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + gobject_class->finalize = gtk_clipboard_finalize; + + class->owner_change = gtk_clipboard_owner_change; + + clipboard_signals[OWNER_CHANGE] = + g_signal_new (I_("owner-change"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkClipboardClass, owner_change), + NULL, NULL, + _gtk_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static void +gtk_clipboard_finalize (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GtkClipboard * +gtk_clipboard_get_for_display (GdkDisplay *display, + GdkAtom selection) +{ + GtkClipboard *clipboard; + + if (selection == GDK_NONE) + selection = GDK_SELECTION_CLIPBOARD; + + if (selection != GDK_SELECTION_CLIPBOARD) + { + g_warning (G_STRLOC ": Only able to create clipboard for CLIPBOARD"); + } + + selection = GDK_SELECTION_CLIPBOARD; + + clipboard = g_object_get_data (G_OBJECT (display), "gtk-clipboard"); + + if (clipboard) + return clipboard; + + clipboard = g_object_new (GTK_TYPE_CLIPBOARD, NULL); + clipboard->display = display; + + g_object_set_data (G_OBJECT (display), "gtk-clipboard", clipboard); + + /* TODO: Need to connect to display closed to free this */ + return clipboard; +} + +GtkClipboard * +gtk_clipboard_get (GdkAtom selection) +{ + return gtk_clipboard_get_for_display (gdk_display_get_default (), selection); +} + + +struct _SetContentClosure { + GtkClipboard *clipboard; + GtkClipboardGetFunc get_func; + GtkClipboardClearFunc clear_func; + guint info; + gpointer userdata; + GtkTargetPair *targets; + gint n_targets; +}; + +static gchar * +_offer_cb (GdkDevice *device, + const gchar *mime_type, + gssize *len, + gpointer userdata) +{ + SetContentClosure *closure = (SetContentClosure *)userdata; + GtkSelectionData selection_data = { 0, }; + guint info = 0; + gint i; + + selection_data.target = gdk_atom_intern (mime_type, FALSE); + + for (i = 0; i < closure->n_targets; i++) + { + if (closure->targets[i].target == selection_data.target) + { + info = closure->targets[i].info; + break; + } + } + + closure->get_func (closure->clipboard, + &selection_data, + info, + closure->userdata); + + *len = gtk_selection_data_get_length (&selection_data); + + /* The caller of this callback will free this data - the GtkClipboardGetFunc + * uses gtk_selection_data_set which copies*/ + return (gchar *)selection_data.data; +} + +static gboolean +gtk_clipboard_set_contents (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data, + gboolean have_owner) +{ + GdkDeviceManager *device_manager; + GdkDevice *device; + gint i; + gchar **mimetypes; + SetContentClosure *closure; + + gtk_clipboard_clear (clipboard); + + device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); + device = gdk_device_manager_get_client_pointer (device_manager); + + closure = g_new0 (SetContentClosure, 1); + closure->clipboard = clipboard; + closure->get_func = get_func; + closure->clear_func = clear_func; + closure->userdata = user_data; + closure->targets = g_new0 (GtkTargetPair, n_targets); + closure->n_targets = n_targets; + + mimetypes = g_new (gchar *, n_targets); + + for (i = 0; i < n_targets; i++) + { + mimetypes[i] = targets[i].target; + closure->targets[i].target = gdk_atom_intern (targets[i].target, FALSE); + closure->targets[i].flags = targets[i].flags; + closure->targets[i].info = targets[i].info; + } + + gdk_wayland_device_offer_selection_content (device, + (const gchar **)mimetypes, + n_targets, + _offer_cb, + closure); + clipboard->last_closure = closure; + + g_free (mimetypes); + return TRUE; +} + +gboolean +gtk_clipboard_set_with_data (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, user_data, + FALSE); +} + +gboolean +gtk_clipboard_set_with_owner (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + GObject *owner) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + g_return_val_if_fail (G_IS_OBJECT (owner), FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, owner, + TRUE); +} + +GObject * +gtk_clipboard_get_owner (GtkClipboard *clipboard) +{ + g_return_val_if_fail (clipboard != NULL, NULL); + + if (clipboard->owner) + return clipboard->owner; + else + return NULL; +} + +void +gtk_clipboard_clear (GtkClipboard *clipboard) +{ + GdkDeviceManager *device_manager; + GdkDevice *device; + + if (!clipboard->last_closure) + return; + + device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); + device = gdk_device_manager_get_client_pointer (device_manager); + + gdk_wayland_device_clear_selection_content (device); + + if (clipboard->last_closure->clear_func) + { + clipboard->last_closure->clear_func (clipboard, + clipboard->last_closure->userdata); + } + + /* TODO: Free last closure */ + clipboard->last_closure = NULL; +} + +static void +text_get_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + gtk_selection_data_set_text (selection_data, data, -1); +} + +static void +text_clear_func (GtkClipboard *clipboard, + gpointer data) +{ + g_free (data); +} + +void +gtk_clipboard_set_text (GtkClipboard *clipboard, + const gchar *text, + gint len) +{ + GtkTargetEntry target = { "text/plain;charset=utf-8", 0, 0 }; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (text != NULL); + + if (len < 0) + len = strlen (text); + + gtk_clipboard_set_with_data (clipboard, + &target, 1, + text_get_func, text_clear_func, + g_strndup (text, len)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); +} + + +static void +pixbuf_get_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + gtk_selection_data_set_pixbuf (selection_data, data); +} + +static void +pixbuf_clear_func (GtkClipboard *clipboard, + gpointer data) +{ + g_object_unref (data); +} + +void +gtk_clipboard_set_image (GtkClipboard *clipboard, + GdkPixbuf *pixbuf) +{ + GtkTargetList *list; + GList *l; + GtkTargetEntry *targets; + gint n_targets, i; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_image_targets (list, 0, TRUE); + + n_targets = g_list_length (list->list); + targets = g_new0 (GtkTargetEntry, n_targets); + for (l = list->list, i = 0; l; l = l->next, i++) + { + GtkTargetPair *pair = (GtkTargetPair *)l->data; + targets[i].target = gdk_atom_name (pair->target); + } + + gtk_clipboard_set_with_data (clipboard, + targets, n_targets, + pixbuf_get_func, pixbuf_clear_func, + g_object_ref (pixbuf)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); + + for (i = 0; i < n_targets; i++) + g_free (targets[i].target); + g_free (targets); + gtk_target_list_unref (list); +} + +typedef struct { + GtkClipboard *clipboard; + GCallback cb; + gpointer userdata; + GdkAtom target; +} ClipboardRequestClosure; + +static void +_request_generic_cb (GdkDevice *device, + const gchar *data, + gsize len, + gpointer userdata) +{ + ClipboardRequestClosure *closure = (ClipboardRequestClosure *)userdata; + GtkClipboardReceivedFunc cb = (GtkClipboardReceivedFunc)closure->cb; + GtkSelectionData selection_data; + + selection_data.selection = GDK_SELECTION_CLIPBOARD; + selection_data.target = closure->target; + selection_data.length = len; + selection_data.data = (guchar *)data; + + cb (closure->clipboard, &selection_data, closure->userdata); + + g_free (closure); +} + +void +gtk_clipboard_request_contents (GtkClipboard *clipboard, + GdkAtom target, + GtkClipboardReceivedFunc callback, + gpointer user_data) +{ + GdkDeviceManager *device_manager; + GdkDevice *device; + ClipboardRequestClosure *closure; + + device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); + device = gdk_device_manager_get_client_pointer (device_manager); + + closure = g_new0 (ClipboardRequestClosure, 1); + closure->clipboard = clipboard; + closure->cb = (GCallback)callback; + closure->userdata = user_data; + closure->target = target; + + /* TODO: Do we need to check that target is valid ? */ + gdk_wayland_device_request_selection_content (device, + gdk_atom_name (target), + _request_generic_cb, + closure); +} + +static void +_request_text_cb (GdkDevice *device, + const gchar *data, + gsize len, + gpointer userdata) +{ + ClipboardRequestClosure *closure = (ClipboardRequestClosure *)userdata; + GtkClipboardTextReceivedFunc cb = (GtkClipboardTextReceivedFunc)closure->cb; + + cb (closure->clipboard, data, closure->userdata); + + g_free (closure); +} + +void +gtk_clipboard_request_text (GtkClipboard *clipboard, + GtkClipboardTextReceivedFunc callback, + gpointer user_data) +{ + GdkDeviceManager *device_manager; + GdkDevice *device; + + ClipboardRequestClosure *closure; + + device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); + device = gdk_device_manager_get_client_pointer (device_manager); + + closure = g_new0 (ClipboardRequestClosure, 1); + closure->clipboard = clipboard; + closure->cb = (GCallback)callback; + closure->userdata = user_data; + gdk_wayland_device_request_selection_content (device, + "text/plain;charset=utf-8", + _request_text_cb, + closure); +} + +void +gtk_clipboard_request_rich_text (GtkClipboard *clipboard, + GtkTextBuffer *buffer, + GtkClipboardRichTextReceivedFunc callback, + gpointer user_data) +{ + /* FIXME: Implement */ +} + +void +gtk_clipboard_request_image (GtkClipboard *clipboard, + GtkClipboardImageReceivedFunc callback, + gpointer user_data) +{ + /* FIXME: Implement */ +} + +void +gtk_clipboard_request_uris (GtkClipboard *clipboard, + GtkClipboardURIReceivedFunc callback, + gpointer user_data) +{ + /* FIXME: Implement */ +} + +void +gtk_clipboard_request_targets (GtkClipboard *clipboard, + GtkClipboardTargetsReceivedFunc callback, + gpointer user_data) +{ + GdkAtom *targets; + gint n_targets; + + gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets); + + callback (clipboard, targets, n_targets, user_data); + + g_free (targets); +} + +typedef struct { + GMainLoop *loop; + GtkSelectionData *selection_data; +} WaitClosure; + +static void +_wait_for_contents_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer userdata) +{ + WaitClosure *closure = (WaitClosure *)userdata; + + if (gtk_selection_data_get_length (selection_data) != -1) + closure->selection_data = gtk_selection_data_copy (selection_data); + + g_main_loop_quit (closure->loop); +} + +GtkSelectionData * +gtk_clipboard_wait_for_contents (GtkClipboard *clipboard, + GdkAtom target) +{ + GdkDeviceManager *device_manager; + GdkDevice *device; + WaitClosure *closure; + GtkSelectionData *selection_data = NULL; + + g_return_val_if_fail (clipboard != NULL, NULL); + g_return_val_if_fail (target != GDK_NONE, NULL); + + device_manager = gdk_display_get_device_manager (clipboard->display); + device = gdk_device_manager_get_client_pointer (device_manager); + + if (target == gdk_atom_intern_static_string ("TARGETS")) + { + GdkAtom *atoms; + gint nr_atoms; + + selection_data = g_slice_new0 (GtkSelectionData); + selection_data->selection = GDK_SELECTION_CLIPBOARD; + selection_data->target = target; + + nr_atoms = gdk_wayland_device_get_selection_type_atoms (device, &atoms); + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_ATOM, 32, + (guchar *)atoms, + 32 * nr_atoms); + + g_free (atoms); + + return selection_data; + } + + closure = g_new0 (WaitClosure, 1); + closure->selection_data = NULL; + closure->loop = g_main_loop_new (NULL, TRUE); + + gtk_clipboard_request_contents (clipboard, + target, + _wait_for_contents_cb, + closure); + + if (g_main_loop_is_running (closure->loop)) + { + GDK_THREADS_LEAVE (); + g_main_loop_run (closure->loop); + GDK_THREADS_ENTER (); + } + + g_main_loop_unref (closure->loop); + + selection_data = closure->selection_data; + + g_free (closure); + + return selection_data; +} + +gchar * +gtk_clipboard_wait_for_text (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gchar *result; + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("text/plain;charset=utf-8")); + + result = (gchar *)gtk_selection_data_get_text (data); + + gtk_selection_data_free (data); + + return result; +} + +GdkPixbuf * +gtk_clipboard_wait_for_image (GtkClipboard *clipboard) +{ + const gchar *priority[] = { "image/png", + "image/tiff", + "image/jpeg", + "image/gif", + "image/bmp" }; + int i; + GtkSelectionData *data; + + for (i = 0; i < G_N_ELEMENTS (priority); i++) + { + data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string (priority[i])); + + if (data) + { + GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data); + + gtk_selection_data_free (data); + + return pixbuf; + } + } + + return NULL; +} + +guint8 * +gtk_clipboard_wait_for_rich_text (GtkClipboard *clipboard, + GtkTextBuffer *buffer, + GdkAtom *format, + gsize *length) +{ + /* FIXME: Implement */ + return NULL; +} + +gchar ** +gtk_clipboard_wait_for_uris (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("text/uri-list")); + if (data) + { + gchar **uris; + + uris = gtk_selection_data_get_uris (data); + gtk_selection_data_free (data); + + return uris; + } + + return NULL; +} + +GdkDisplay * +gtk_clipboard_get_display (GtkClipboard *clipboard) +{ + g_return_val_if_fail (clipboard != NULL, NULL); + + return clipboard->display; +} + +gboolean +gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_text (data); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_is_rich_text_available (GtkClipboard *clipboard, + GtkTextBuffer *buffer) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_rich_text (data, buffer); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_image (data, FALSE); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_is_uris_available (GtkClipboard *clipboard) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + if (data) + { + result = gtk_selection_data_targets_include_uri (data); + gtk_selection_data_free (data); + } + + return result; +} + +gboolean +gtk_clipboard_wait_for_targets (GtkClipboard *clipboard, + GdkAtom **targets, + gint *n_targets) +{ + GtkSelectionData *data; + gboolean result = FALSE; + + g_return_val_if_fail (clipboard != NULL, FALSE); + + data = + gtk_clipboard_wait_for_contents (clipboard, + gdk_atom_intern_static_string ("TARGETS")); + + if (data) + { + GdkAtom *tmp_targets; + gint tmp_n_targets; + + result = gtk_selection_data_get_targets (data, + &tmp_targets, + &tmp_n_targets); + + if (n_targets) + *n_targets = tmp_n_targets; + + if (targets) + *targets = tmp_targets; + else + g_free (tmp_targets); + + gtk_selection_data_free (data); + } + + return result; +} + +static void +gtk_clipboard_owner_change (GtkClipboard *clipboard, + GdkEventOwnerChange *event) +{ + +} + +gboolean +gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard, + GdkAtom target) +{ + GdkAtom *targets; + gint i, n_targets; + gboolean retval = FALSE; + + g_return_val_if_fail (clipboard != NULL, FALSE); + + if (!gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets)) + return FALSE; + + for (i = 0; i < n_targets; i++) + { + if (targets[i] == target) + { + retval = TRUE; + break; + } + } + + g_free (targets); + + return retval; +} + +void +_gtk_clipboard_handle_event (GdkEventOwnerChange *event) +{ +} + +void +gtk_clipboard_set_can_store (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + gint n_targets) +{ + /* FIXME: Implement */ +} + +void +gtk_clipboard_store (GtkClipboard *clipboard) +{ + /* FIXME: Implement */ +} + +void +_gtk_clipboard_store_all (void) +{ + /* FIXME: Implement */ +} -- 2.30.2